﻿//  -------------------- iSharePoint -------------------- //
// 
//  Company:	IT Complex, LLC
//  Project:	Smart.Common
//  File name:	FormsService.cs
//  Developer:	Solomatov Igor
//  Created:	15.04.2012
// 
//  -------------------- iSharePoint -------------------- //

using System;
using System.Collections.Generic;
using System.Security;

namespace Smart.Common.Forms
{
	/// <summary>
	/// Forms service
	/// </summary>
	public abstract class FormsService : IFormsService
	{
		/// <summary>
		/// Forms context
		/// </summary>
		public FormsContext Ctx { get; set; }

		/// <summary>
		/// Forms source (lazy)
		/// </summary>
		public FormsSource Source { get; set; }

		/// <summary>
		/// Forms formatter (lazy)
		/// </summary>
		public FormsFormatter Formatter { get; set; }

		/// <summary>
		/// Forms builder (lazy)
		/// </summary>
		public FormsBuilder Builder { get; set; }

		/// <summary>
		/// Creates forms context
		/// </summary>
		public virtual void InitContext(string serviceName)
		{
			Ctx = new FormsContext(serviceName) { Service = this };
		}

		/// <summary>
		/// Init service (also fills lazy properties)
		/// </summary>
		public virtual void InitService()
		{
			if (Source == null)
				Source = CreateSource();

			if (Formatter == null)
				Formatter = CreateFormatter();

			if (Builder == null)
				Builder = CreateBuilder();
		}

		/// <summary>
		/// Create forms source
		/// </summary>
		public abstract FormsSource CreateSource();

		/// <summary>
		/// Create forms formatter
		/// </summary>
		public abstract FormsFormatter CreateFormatter();

		/// <summary>
		/// Create forms builder
		/// </summary>
		public abstract FormsBuilder CreateBuilder();

		/// <summary>
		/// Perform action from rules on form
		/// </summary>
		public virtual bool PerformAction(string actionName, string[] actionArgs, out bool handled)
		{
			handled = false;
			return false;
		}

		/// <summary>
		/// Execute rule condition function
		/// </summary>
		public virtual bool ExecuteFunction(string functionName, string[] functionArgs, out bool handled)
		{
			handled = false;
			return false;
		}

		/// <summary>
		/// Checks whether form allowed to access
		/// </summary>
		/// <returns>true, if allowed</returns>
		protected virtual bool CheckSecurity()
		{
			return true;
		}

		/// <summary>
		/// Load service values in context
		/// </summary>
		protected virtual void LoadServiceValuesToCtx()
		{
			Ctx.Values["Form:Mode"] = Ctx.Mode;
			Ctx.Values["Form:Type"] = Ctx.FormsTypeName;

			if (Ctx.Meta != null)
				Ctx.Values["Form:Meta:Name"] = Ctx.Meta.Name;
		}

		/// <summary>
		/// Load form values in context
		/// </summary>
		protected virtual void LoadFormValuesToCtx()
		{
			Ctx.Values["Form:Name"] = Ctx.Form.Name;
		}

		/// <summary>
		/// Build form for <paramref name="entity"/> in <paramref name="container"/>
		/// </summary>
		public virtual bool BuildForm(IFormEntity entity, IFormContainer container, MetaFormMode mode, IDictionary<string, object> values)
		{
			if (entity == null)
				throw new ArgumentNullException("entity");

			if (container == null)
				throw new ArgumentNullException("container");

			Ctx.Entity = entity;
			Ctx.Container = container;
			Ctx.Mode = mode;

			// Detect metadata
			Ctx.Meta = Source.DetectMetaList();

			// Detect forms type (to load definition from)
			Ctx.FormsTypeName = Source.DetectFormsType();

			if (Ctx.FormsTypeName.IsNullOrEmpty())
				return false;

			OnCtxInited();

			// Load forms definition
			var formsDefinition = Source.LoadFormsDefinition(Ctx.FormsTypeName);

			if (formsDefinition == null)
				throw new NotSupportedException("FormsTypeName");

			OnDefinitionLoaded();

			// Parse forms definition
			Ctx.Forms = Builder.ParseFormsDefinition(formsDefinition);

			if (Ctx.Forms == null)
				throw new NotSupportedException("FormsDefinition");

			OnDefinitionParsed();

			// Load service values in context
			LoadServiceValuesToCtx();
			// Load entity values in context
			Source.LoadEntityValuesToCtx();
			// Load user values in context
			Source.LoadValuesToCtx(values);

			OnCtxValuesLoaded();

			// Detect rules service name
			Ctx.ChooseRulesServiceName = Builder.DetectFormsChooserRulesServiceName();
			// Choose active form
			Ctx.Form = Builder.ChooseForm();

			if (Ctx.Form == null)
				throw new NotSupportedException("ChooseForm");

			// Load form values in context
			LoadFormValuesToCtx();

			OnFormChoosed();

			// Check security
			if (!CheckSecurity())
				throw new SecurityException();

			OnSecurityChecked();

			// Detect rules service name
			Ctx.ActionRulesServiceName = Formatter.DetectFormActionRulesServiceName();

			// Fill active form with all values
			Builder.FillFormValues();
			// Process form after form values filled
			Builder.ProcessFormAfterFill();

			OnProcessAfterFill();

			// Instantiate form in container
			Formatter.InstantiateFormInContainer();
			// Load current values
			Formatter.LoadValuesAfterInstantiate();
			// Process form after form values filled
			Formatter.ProcessFormAfterLoad();

			OnProcessAfterLoad();

			// Process form rules
			Formatter.ProcessFormRules();
			// Process form after form rules processed
			Formatter.ProcessFormAfterRules();

			OnProcessAfterRules();

			return true;
		}

		/// <summary>
		/// Save form values to entity
		/// </summary>
		public virtual bool SaveForm()
		{
			if (Ctx.Form == null || Ctx.Container == null || Ctx.Container.Controls == null)
				throw new InvalidOperationException();

			Formatter.SaveValues();

			return
				Source.SaveEntityValuesFromCtx();
		}

		protected virtual void OnProcessAfterRules()
		{
			FireEvent(Const.EventNames.Forms.FormsProcessAfterRules);
		}

		protected virtual void OnProcessAfterLoad()
		{
			FireEvent(Const.EventNames.Forms.FormsProcessAfterLoad);
		}

		protected virtual void OnProcessAfterFill()
		{
			FireEvent(Const.EventNames.Forms.FormsProcessAfterFill);
		}

		protected virtual void OnSecurityChecked()
		{
			FireEvent(Const.EventNames.Forms.FormsSecurityChecked);
		}

		protected virtual void OnFormChoosed()
		{
			FireEvent(Const.EventNames.Forms.FormsFormChoosed);
		}

		protected virtual void OnCtxValuesLoaded()
		{
			FireEvent(Const.EventNames.Forms.FormsCtxValuesLoaded);
		}

		protected virtual void OnDefinitionParsed()
		{
			FireEvent(Const.EventNames.Forms.FormsDefinitionParsed);
		}

		protected virtual void OnDefinitionLoaded()
		{
			FireEvent(Const.EventNames.Forms.FormsDefinitionLoaded);
		}

		protected virtual void OnCtxInited()
		{
			FireEvent(Const.EventNames.Forms.FormsCtxInited);
		}

		/// <summary>
		/// Fire event via SPServices.EventManager
		/// </summary>
		protected virtual bool FireEvent(string eventTempl)
		{
			SPServices.EventManager.FireEvent(string.Format(eventTempl, Ctx.ServiceName), Ctx, this);

			return true;
		}
	}
}